home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 25 / Cream of the Crop 25.iso / bbs / doorinfo.zip / CSWITCH.DOC < prev    next >
Text File  |  1997-05-14  |  56KB  |  1,492 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.                           C S W I T C H
  22.                         -----------------
  23.  
  24.  
  25.  
  26.  
  27.  
  28.                Multitasking Functions for DOS Programs
  29.  
  30.                   Copyright 1990-1993 by Herb Rose
  31.                         All Rights Reserved
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.               Version 3.0 release date : Aug 29, 1992
  57.  
  58.  
  59. This file is an introduction to the Cswitch functions.  These
  60. functions provide limited multitasking capabilities for programs
  61. running under MS-DOS or PC-DOS.
  62.  
  63. The term 'limited multitasking' is used because these routines do not
  64. provide a multitasking user interface like DESQVIEW or WINDOWS.  These
  65. routines are programming tools that will enable a C programmer to
  66. write applications that consist of independent, simultaneously executing
  67. programs.
  68.  
  69. Using Cswitch, you can execute functions as independent tasks, which
  70. execute concurrently in a time-slicing environment.  You may also load
  71. and execute programs separately.  Cswitch provides you with a priority
  72. driven scheduler and dispatcher, DOS interface, and task control
  73. functions to implement true multitasking under your program's control.
  74.  
  75. Before I continue the explanation of Cswitch, please bear with me as
  76. I detail the legalities of using these routines.
  77.  
  78.  
  79. WARRANTY
  80. --------
  81.  
  82. This software is distributed as is, with no warranty of any kind,
  83. either expressed or implied.  No warranty or guarantee is made that
  84. the software is complete or error free.  All liability and risk
  85. associated with the use of this software is assumed by the user.
  86. Under no circumstances will ADEPT Software, its owners, or its
  87. agents be responsible for any damages arising from the use, or the
  88. inability to use this product.  This applies to all damages and
  89. remedies including, but not limited to, loss of or damage to property,
  90. and loss of profits.
  91.  
  92. This software has been tested in many configurations, under various
  93. conditions, and has consistently performed acceptably.  To the best
  94. of my knowledge, there are no errors or omissions in the software
  95. that will pose a problem to the user.
  96.  
  97. The user must be aware, however, that this is a programmer's tool,
  98. and by it's very nature will affect the software that controls the
  99. user's computer.  All reasonable precautions should be taken to
  100. protect your computer from the affects of an errant operating system.
  101.  
  102. In short -
  103. BACK UP YOUR DISKS, AND BE CAREFUL WHEN TESTING NEW PROGRAMS THAT
  104. USE CSWITCH!!!!!
  105.  
  106.  
  107. Version 1.1 Update Information
  108.  
  109. When loading programs from disk, Cswitch now searches the current
  110. directory, and those directories specified in the DOS PATH environment
  111. variable.
  112.  
  113. There was a report of Cswitch not working properly with PS/2 Model 70
  114. computers.  The report suggested a problem with the MCA failsafe
  115. timer.  I have not yet been able to verify that there is a problem,
  116. but will continue to monitor and test.
  117.  
  118.  
  119. Version 2.0 Update Information
  120.  
  121. Semaphore logic corrected, and new return codes added.  In older versions
  122. the semaphore calls (sem_attach, sem_release, and sem_test) did not
  123. correctly test whether the semaphore number passed in was legal, and
  124. did not return the proper result codes if the semaphore number was
  125. illegal, or if the semaphore was currently attached.
  126.  
  127. Version 2.1 Update Information
  128.  
  129. Corrected a system bug that caused system lockups when tasks other than
  130. the 'root' task attempted to load a program from disk.  This is now
  131. working correctly.
  132.  
  133. Corrected a bug with searching PATH directories when loading tasks.
  134. Also, made the search include both .COM and .EXE files, where previously
  135. you had to explicitly state .COM in the filename (.EXE was the default).
  136.  
  137. Added 'pipe tasks'.  Similar to POPEN() under unix, you can now load
  138. and execute a program, and have it's STDIN, STDOUT and STDERR directed
  139. to a CSWITCH pipe.  The pipe is a 2-way circular queue, holding 1024
  140. bytes in either direction.  The program loaded with 'LOADPIPE()' will
  141. issue normal I/O calls to it's STDIN, STDOUT, and STDERR files.  The
  142. calling task can write to the pipe with SEND_PIPE() and receive data
  143. from the pipe with RECV_PIPE().  This allows a standard DOS task, which
  144. uses normal printf(), gets(), getchar() type I/O calls, to be executed
  145. under control of another program.
  146.  
  147. Added EXECPROG() call.  This will load in a new task, and the new task
  148. will replace the caller.  This is especially useful for tasks loaded
  149. via 'LOADPIPE()', since the pipe is inherited by the new task.
  150. There is no return value if the load is successful.
  151.  
  152. Version 3 update information
  153.  
  154. Corrected some memory allocation problems, and added support for the
  155. Turbo C /Borland C++ trick for finding out how much memory is available.
  156. This trick is to change DOS's allocation strategy to 'last fit', then
  157. allocate 1 paragraph of memory, and use the address of that paragraph
  158. as the highest possible memory address.
  159.  
  160. When using Microsoft C, versions 5.1-7.0, you must use the /Au switch to
  161. tell the compiler that is should NOT assume that DS and SS are always
  162. equal.  This was causing a real problem with programs loaded by any
  163. program execpt the main task.
  164.  
  165.  
  166.  
  167.  
  168. Shareware Distribution
  169. ----------------------
  170.  
  171. CSWITCH is distributed as shareware.  You may freely use and distribute
  172. these routines under the following conditions :
  173.  
  174.     1. All files on this diskette must be distributed as a set, and no
  175.        modifications or additions may be distributed as part of the
  176.        set unless specifically approved in writing by Herb Rose.
  177.  
  178.     2. All copyright and legal notices must remain intact and unaltered.
  179.  
  180.     3. Copying and distribution fees may not exceed $6, and must be
  181.        clearly specified as such.
  182.  
  183.     4. You must register Cswitch if you want a copy of the source code,
  184.        or if you wish to distribute a software product that makes use
  185.        of any of the Cswitch functions.
  186.  
  187. Note that registration is not required if you simply want to use Cswitch
  188. in the privacy of your own home, or just to see what it can do.  As an
  189. incentive to registration, I include C and assembly source code when you
  190. register.  The source code is easy to read and well documented.  Make
  191. files are included for Microsoft C and Microsoft Assembler.
  192.  
  193. Registered users are informed of new releases of the software, and may
  194. download new releases (including source code) from the INFO*SHARE BBS.
  195.  
  196. If you plan to distribute any software product that makes use of Cswitch
  197. functions, then registration is mandatory.  Registration provides you
  198. with an unlimited, nonexclusive binary distribution license, with no
  199. royalties.  This is only fair, I think.  If you distribute a program,
  200. you should pay for the tools you used to create that program.
  201.  
  202. Registration is for use on a single computer.  Contact me
  203. for site licensing, or for multiple copy discounts.
  204.  
  205. Due to practical constraints, I cannot provide telephone assistance
  206. except to registered users.  I operate a 24-hour Bulletin Board System
  207. to handle technical assistance.  Telephone support is available for
  208. registered users, but I prefer to handle non-emergency questions via
  209. the BBS.  The number is  (703) 803-8000.
  210.  
  211.  
  212.  
  213. Registration Form
  214. -----------------
  215.  
  216. Please use the following form, or a reasonable facsimile, when
  217. registering Cswitch.
  218.  
  219.  
  220.             Mail to :    Herb Rose
  221.                          P.O. Box 906
  222.                          Centreville, Va.  22020
  223.  
  224.  
  225.     CSWITCH Registration ..................... ______ @ $50 = ______
  226.  
  227.     Virginia Residents add 4.5% sales tax ................. = ______
  228.  
  229.     Overseas Air Delivery ...............................$5 = ______
  230.       (U.S. delivery and Overseas Surface delivery is free)
  231.  
  232.     TOTAL ................................................. = ______
  233.  
  234.         Please send check or money order, in U.S. funds.
  235.  
  236.  
  237.         Your Name :        _____________________________________
  238.  
  239.         Company Name :     _____________________________________
  240.  
  241.         Address :          _____________________________________
  242.  
  243.         City, State, Zip : _____________________________________
  244.         (or country)
  245.  
  246.  
  247.  
  248.  
  249.  
  250.  
  251. Introduction to Multitasking
  252. ----------------------------
  253.  
  254. Multitasking is not a new concept.  Computers have been programmed to
  255. execute multiple tasks concurrently for many years.  It was observed
  256. a long time ago that most programs spent the majority of their time
  257. waiting for data input or output.  Since users and most peripheral devices
  258. are notoriously slow compared to the execution speeds of modern computers,
  259. most programs simply sit there 'twiddling their thumbs' waiting for data.
  260. It was observed that the computer's computational capabilities could be
  261. used more efficiently if another programs were allowed to use the processor
  262. while a program that was just waiting was put into a dormant state.
  263. Carrying this observation one step farther, we find true multitasking.
  264. Multitasking is the ability to have several programs loaded into the
  265. computers memory, and apparently executing simultaneously.  In reality,
  266. there is a controlling program, called the Kernel, or Scheduler, that
  267. causes the computer to execute some of each programs instructions in
  268. a round-robin fashion.  Programs that are waiting for something to
  269. happen, such as waiting for a keystroke, or for a disk transfer to
  270. complete, are normally placed into a 'waiting' state, and do not get
  271. placed back into execution until the event they are waiting for has
  272. actually happened.
  273.  
  274. Notice that I said programs were 'apparently' running simultaneously.
  275. It is impossible for a microcomputer, such as the PC, to execute more
  276. that one program simultaneously.  Other computers may have several micro-
  277. processors, and can actually execute more than one task simultaneously.
  278. When there is only one microprocessor, the tasks must be executed one
  279. at a time.  The term 'time-slicing' is used to describe the operation
  280. of a multi-tasking operating system, meaning that the processor will
  281. execute each task's instructions for a short time, then will execute
  282. another task's instructions for a while, eventually allowing all the
  283. tasks to get some of their work done in a round-robin fashion.  If the
  284. amount of time given to each task is very large, say one or two seconds,
  285. there could be a significant amount of time for each task to remain
  286. idle while it awaited it's turn to execute.  If the amount of time
  287. given to each task (called the 'time slice') is too short, then too
  288. much of the computer's time will be spent saving and restoring task
  289. context information, diminishing the efficiency of the system.  A
  290. good time slice setting is one that allows many different tasks to
  291. execute in a short period of time, without noticably affecting the
  292. efficiency of the system.  Cswitch uses the timer interrupt for the
  293. PC to trigger a task switch.  This occurs approximately 18 times per
  294. second, or once every 55 milliseconds.  Since a normal time slice on
  295. a real multitasking operating system is usually around 50 microseconds,
  296. our time slice is actually very large.  In fact, 55 milliseconds is
  297. way too big for a real-time operating system, which has to react to
  298. external stimulus as quickly as possible.  With 18 tasks loaded into
  299. Cswitch, each task will only get to run once per second, for about
  300. 1/18 second.  If some of the tasks are higher priority than others,
  301. the lower priority tasks may not run at all for several seconds.  This
  302. is clearly not acceptable for programs that must react to external
  303. stimulus.
  304.  
  305. The 55 millisecond time slice, however, is fine for running one or
  306. two tasks at a high priority, and allowing several other tasks to
  307. run at a lower priority.  This gives the effect of 'background'
  308. processes running.
  309.  
  310. If you find the 55 millisecond time slice inadequate, you should
  311. consider altering the timer chip's programming so that it provides
  312. a timer interrupt more often.  Your interrupt handler will have to
  313. count the number of interrupts, and activate the DOS time-of-day
  314. routine (the normal destination of interrupt vector 8) once every
  315. 55 milliseconds, otherwise the computer's internal date and time
  316. settings will not be accurate.
  317.  
  318. Now we will focus on how you get multiple tasks to cooperate so that a
  319. given job will get done.  Basically, there are only 2 areas that we are
  320. concerned with -
  321.  
  322. 1. Passing information from one task to another, and
  323. 2. Preventing another task from interfering with a resource you are using.
  324.  
  325.  
  326. Moving Data Between Tasks
  327. -------------------------
  328.  
  329. The first area is handled in Cswitch via Message Queues.  A queue is
  330. simply a list of messages.  In this case, Cswitch has 128 queues, numbered
  331. 0-127.  Any task may place a message onto any of the queues, and any
  332. task may fetch a message from any of the queues.  If there is more than
  333. one message waiting on a queue, they will be fetched sequentially, on a
  334. first-in, first-out basis.
  335.  
  336. A message may be any format, containing any information that may be needed
  337. by another task.  When placing a message on a queue, you must tell Cswitch
  338. which queue to put it on, how long the message is (in bytes), and the
  339. address of the first byte of the message.  Cswitch will copy as many
  340. bytes as you have told it into a temporary buffer, and will place a
  341. pointer to that buffer into the message list for that queue.
  342.  
  343. When you fetch a message from a queue, you must tell Cswitch which queue
  344. to read a message from, the maximum number of bytes you are willing to
  345. receive, and where to put the message.  After Cswitch has copied the
  346. message (or as much of the message as you will allow) into your memory,
  347. the temp buffer is released, and the message pointer is deleted from
  348. the queue's list.  Note : if the message contained more bytes than
  349. you received (by specifying a maximum byte count lower than the actual
  350. size of the message), the message will still be removed from the
  351. queue.  You should make certain you allow enough room to receive any
  352. message your task is likely to receive.
  353.  
  354. It is up to the individual tasks to coordinate which queue numbers will
  355. be used to pass messages back and forth.  In the Falken BBS, a multi-
  356. line Bulletin Board System program written with Cswitch, the main
  357. BBS program assigns input and output queues to each task when it is
  358. started.  Queue numbers 0 and 1 are reserved, used to send the queue
  359. assignments to the tasks.
  360.  
  361. Pipes
  362. -----
  363.  
  364. Pipes are also used to transfer data between tasks.  In this case,
  365. the 'parent' task uses the LOADPIPE() service to load in a new task
  366. from disk, and automatically create an internal pipe for the new task.
  367. All I/O for the new tasks STDIN, STDOUT, and STDERR (normally the
  368. keyboard and monitor) will now be redirected to the pipe.
  369.  
  370. This means that when a program loaded with LOADPIPE() performs a
  371. 'GETS()' call to read a string from the keyboard, it will actually
  372. attempt to get the string from it's pipe.  The parent task sends
  373. data to the pipe using the 'SEND_PIPE()' service, and retrieves the
  374. new tasks output using the 'RECV_PIPE()' service.  In this way, a
  375. normal DOS task can be executed as a utility for a larger task.
  376. The pipe is associated with the new tasks TCB, and will be removed
  377. when the new task terminates.
  378.  
  379. Semaphores
  380. ----------
  381.  
  382. The second area of concern is to prevent another task from interfering
  383. with a resource that you are using.  A resource may be almost anything,
  384. from a disk file to a memory buffer, to a peripheral device.  Cswitch
  385. uses a semaphore system to control access to these resources.
  386.  
  387. You must know in advance which resources must be controlled with
  388. semaphores.  If you are going to allow several tasks to access a
  389. memory area simultaneously, it may be a good idea to control it, since
  390. one task may try to write new information to the memory while another
  391. task is trying to read the old information.
  392.  
  393. Semaphores work like this -
  394. A semaphore has an owner and a waiting list.  If your task attempts to
  395. 'attach' to the semaphore, but another task already 'owns' the semaphore,
  396. your task gets placed onto the 'waiting list', and does not get to run
  397. anymore (it is taken out of the ready queue).  Your task will remain
  398. dormant until it is the 'owner' of the semaphore.
  399.  
  400. When the owner of the semaphore 'releases' it, the first task in the
  401. 'waiting list' is made the new owner, and is allowed to run again.
  402.  
  403. Note : When you 'attach' to a semaphore, you will become the owner of
  404. the semaphore before you are allowed to continue.  Your
  405. program remains dormant until it is the owner, and you must release the
  406. semaphore when you are done with it, to allow other programs to run,
  407. which may be waiting for the semaphore.
  408.  
  409. If you attempt to 'attach' to a semaphore, and it is unowned, your
  410. task becomes the owner and is allowed to continue running immediately.
  411. You must still release the semaphore when you are done.
  412.  
  413.  
  414. Cswitch Functions
  415. -----------------
  416.  
  417. Cswitch allows tasks to perform the following functions :
  418.  
  419.     - All normal DOS and BIOS functions
  420.     - Send information between tasks with Message Queues
  421.     - Control system resources with Semaphores
  422.     - Alter task priority
  423.     - Suspend / Resume task execution
  424.     - Delay for a time period (sleep)
  425.     - Start new tasks, either sharing existing code, or loading a new
  426.       program file from disk.
  427.     - Load small programs into expanded memory to allow more programs
  428.       to execute simultaneously.
  429.  
  430. To use Cswitch, you must link your main program with the CSWITCH1.OBJ,
  431. CSWITCH2.OBJ and LMTC.OBJ files.  These files provide interrupt level
  432. control for task switching, and the operating system functions, such as
  433. message passing and semaphore control.  In addition, programs which
  434. will be loaded and executed under control of Cswitch must be linked
  435. with LMTC.OBJ or SMTC.OBJ (for large or small model, respectively).
  436. These files contain interface routines for calling system services.
  437.  
  438. Your main program must call the function START_MT().
  439.  
  440. START_MT() initializes the Cswitch data areas and enables multitasking.
  441. It intercepts several interrupt vectors, and builds Task Control
  442. Blocks for your main program and for an 'idle task'.  The idle task
  443. runs at a low priority, and checks the delay and termination queues
  444. regularly to service tasks waiting for those functions.
  445. After the call to START_MT(), multitasking is fully operational,
  446. and you may spawn tasks and load programs.  Note that your main
  447. program has a Task Control Block (TCB) and gets scheduled for time-
  448. slicing just as every other task does. Your main program runs at a
  449. priority of 1 (the highest priority allowed).
  450.  
  451. Before terminating, your main program MUST call the function END_MT().
  452. END_MT restores the interrupt vectors, releases all unnecessary
  453. memory, and halts all multitasking.  Essentially, it restores the
  454. computer to the same state it was in prior to the START_MT() call.
  455.  
  456. ** WARNING ** WARNING ** WARNING ** WARNING ** WARNING ** WARNING **
  457.  
  458. If you allow your program to terminate without calling END_MT, almost
  459. anything can happen, since the interrupt vectors are still pointing
  460. to Cswitch code which may or may not be overwritten by DOS.  At a
  461. minimum, your system will lock up.  A worst-case scenario could
  462. involve unrecoverable damage to disk files.
  463.  
  464. It is highly recommended that you test all your programs that use
  465. multitasking on a 'test' computer, a separate machine that does not
  466. contain any critical files or programs, to prevent loss of valuable
  467. data or programs.  At a minimum, make sure all your important files
  468. are backed up before testing new programs that use Cswitch routines.
  469.   
  470.  
  471. Executing Tasks
  472. ---------------
  473.  
  474. There are 2 methods of starting new tasks under Cswitch.  The first
  475. method is to SPAWN a new task that will execute a portion of your code
  476. independently.  Normally, this facility will be used to execute a
  477. function as a separate task.  The new task will be given it's own
  478. stack and TCB, and will be scheduled for time-slicing normally.  It
  479. will begin executing code at the address you specify (normally a
  480. function name).  It may call other functions, and may manipulate
  481. global data normally.  Any automatic (local) variables will reside
  482. on the new tasks stack, so there will not be any interference when
  483. a function is called by 2 different tasks.  In fact, you can spawn
  484. several new tasks which execute the same function if you like.
  485.  
  486. When the function being executed returns to the caller, the task will
  487. be terminated.  Any functions that are called by the spawned task will
  488. return normally, of course.  Only when the function being spawned
  489. returns will the task terminate.
  490.  
  491. The second method of starting new tasks is to load a program from
  492. disk and execute it.  The function LOADTASK() is used to load an EXE
  493. or COM file from disk and execute it.  You must supply 3 parameters
  494. to the LOADTASK function - the command line to run the program and
  495. the priority to be assigned to the task, and a flag indicating whether
  496. the task may be loaded into expanded memory or not.
  497.  
  498. For instance, to load and run the program MYPROG.EXE with the parameter
  499. 'myfile.dat', you would normally enter a DOS command line like this :
  500.  
  501. C:>myprog myfile.dat
  502.  
  503. To load and run the program under Cswitch, you would call the LOADTASK
  504. function like this :
  505.  
  506.     loadtask("myprog myfile.dat",4,1);
  507.  
  508. This loads and runs 'myprog.exe', gives it the command line parameter
  509. 'myfile.dat', and allows it to run at priority 4, and allows it to be
  510. loaded into expanded memory, if possible.
  511.  
  512. The .EXE extension is assumed if no extension is provided.  You can
  513. load and execute .COM files by specifying the .COM extension.
  514. The executable file must be in the current directory, or in a
  515. directory referenced by your PATH variable.
  516.  
  517.  
  518. Scheduling
  519. ----------
  520.  
  521. The Cswitch scheduler maintains a list of tasks that are waiting to
  522. execute, called the 'ready queue'.  When a task switch occurs, the
  523. TCB at the head of the ready queue is fetched, and allowed to run.
  524. Normally, the TCB that was just executing is inserted back into the
  525. ready queue.  In some cases, such as waiting for semaphore, delaying,
  526. etc. the task will not be re-inserted into the ready queue.
  527.  
  528. Tasks are inserted into the ready queue according to their relative
  529. priority.  Each task is assigned a 'base priority' from 1 to 10.
  530. 1 is the highest priority, and 10 is the lowest.  Each task also has
  531. a 'current priority', which is used to insert TCBs into the ready
  532. queue. Each time a task is fetched from the ready queue, the 'current
  533. priority' of all the other TCBs on the ready queue is decremented
  534. by 1.  When a task is re-inserted into the ready list, it's current
  535. priority is set equal to it's base priority, then it is inserted
  536. into the ready queue so that it is in front of all tasks whose current
  537. priority is lower.  In this way, tasks with a higher priority will
  538. be allowed to execute more often than tasks of lower priority.  The
  539. tasks with lower priority will remain on the ready queue until their
  540. current priority increases to the point that the other tasks get
  541. inserted behind them, and they work their way to the head of the queue.
  542.  
  543.  
  544. Using Expanded Memory
  545. ---------------------
  546.  
  547. By calling START_SWAPPING(), you tell Cswitch to start using expanded
  548. memory.  Any task that is loaded after calling START_SWAPPING() is
  549. eligible to be loaded into expanded memory.
  550.  
  551.  
  552. Cswitch System Services
  553. -----------------------
  554.  
  555. Cswitch must replace some of the DOS system services with it's own
  556. services.  In every case, the calling sequence and register setup
  557. is identical do the DOS call.  Return values are also identical
  558. to DOS return values.  In short, if your program ran OK under DOS,
  559. it should run OK under Cswitch.
  560.  
  561. Cswitch replaces the following DOS (interrupt 21) services :
  562.  
  563.     48h : allocate memory
  564.  
  565.         Cswitch controls memory allocation for all tasks.  DOS uses
  566.         a 'first fit' algorithm for allocating memory, meaning that
  567.         it allocates a chunk of memory from the first block that
  568.         is large enough to fulfill the request.  This leads to memory
  569.         fragmentation, limiting the number of tasks that can run.
  570.         Cswitch uses a 'best fit' algorithm, searching all the
  571.         available memory blocks to find a block that is exactly the
  572.         size needed, or is closest to it.  This way, memory fragmentation
  573.         is kept to a minimum.
  574.  
  575.         Note : starting with DOS 3.3, you can alter the memory
  576.         allocation strategy that DOS uses, but 'first fit' is still
  577.         the default.
  578.  
  579.     49h : release memory
  580.  
  581.         Cswitch must also handle the memory release function.  When a
  582.         block of memory is released, all memory is re-combined as much
  583.         as possible to limit memory fragmentation.
  584.  
  585.     4ah : set memory block size
  586.  
  587.         This DOS function is used to adjust the size of a memory block.
  588.         Since DOS tasks expect to be the only task running, they assume
  589.         that a memory block can be altered by simply increasing the size
  590.         of the block when they need more memory.  Since several tasks
  591.         may occupy memory blocks adjacent to the block to be modified,
  592.         this is not always possible under Cswitch.  Cswitch will attempt
  593.         to fulfill the request by looking for a free block adjacent to
  594.         the specified block, and combining them to form a larger block.
  595.         If unsuccessful, it will return an error.
  596.  
  597.         Note that programs that allocate memory dynamically with CALLOC
  598.         or MALLOC calls may run into problems with this limitation.
  599.         The problem is that the library routines for allocating memory
  600.         (MALLOC, et al) use a table of structures to control the memory
  601.         blocks they obtain from DOS.  Normally, this table will only
  602.         hold 20 entries (this is true for Microsoft C and QuickC).
  603.         Since Cswitch will return an error on some of the 'set memory
  604.         block' calls, the library routines may execute an 'allocate
  605.         memory' call every time you execute MALLOC.  After 20 calls,
  606.         the table is full, and you can't allocate any more memory!
  607.  
  608.         You can avoid problems by using the EXEMOD program to change
  609.         the minimum memory requirement of your task, so that enough
  610.         memory will be reserved when your task is loaded to handle
  611.         dynamic memory allocation demands without having to request
  612.         more memory from DOS.
  613.  
  614.         Or, you can avoid using the MALLOC/CALLOC routines wherever
  615.         possible, and instead call the DOS allocate memory call
  616.         directly.  Be careful, though, because Cswitch can only
  617.         control a total of 512 memory blocks.
  618.  
  619.         When a task in expanded memory wants to extend the memory block,
  620.         it is much easier to do, provided the page frame size is not exceeded.
  621.         Since each task loaded into expanded memory is allocated the
  622.         entire 64K expanded memory region to work in, it is usually
  623.         possible to extend the memory allocation to the desired size.
  624.  
  625.     4ch : terminate task
  626.  
  627.         Cswitch handles task termination internally. It will release
  628.         memory held by the task, close all open files, and release the
  629.         Task Control Block.
  630.  
  631.     1ah : set new DTA area
  632.  
  633.         Cswitch records the address of the tasks new DTA in the TCB, then
  634.         passes this request to DOS normally.  This function is normally
  635.         handled by the library routines, and is seldom called explicitly
  636.         by a programmer.
  637.  
  638.     2fh : get DTA address
  639.  
  640.         Cswitch returns the DTA address from the tasks TCB.
  641.  
  642.  
  643. Cswitch Functions
  644. -----------------
  645.  
  646. The following functions are provided in the file CSWITCH1.C, and may
  647. only be called by the main program. i.e. these functions may only be
  648. called by the program that is linked with Cswitch1 and Cswitch2.
  649.  
  650.     START_MT()
  651.  
  652.         Parameters :
  653.                 none
  654.  
  655.         Description :
  656.                 Starts the multitasking kernel, and enables all system
  657.                 functions.  This function must be called once, and only
  658.                 once, before executing any of the other functions or
  659.                 system services.
  660.  
  661.         Return Value :
  662.                 none
  663.  
  664.         Notes/Comments :
  665.  
  666.  
  667.     START_SWAPPING()
  668.  
  669.         Parameters :
  670.                 none
  671.  
  672.         Description :
  673.                 This function sets Cswitch up to swap tasks out to
  674.                 expanded memory.
  675.  
  676.         Return Value :
  677.                 The amount of expanded memory available is returned,
  678.  
  679.         Notes/Comments :
  680.                 All tasks started prior to this call are memory-resident,
  681.                 and all spawned tasks are memory resident.  This routine
  682.                 may only be called once, and there is no way to turn
  683.                 off usage of expanded memory  once it is started.
  684.  
  685.  
  686.     END_MT()
  687.  
  688.         Parameters :
  689.                 none
  690.  
  691.         Description :
  692.                 Ends multitasking.  The program that called START_MT()
  693.                 must call END_MT() before terminating.
  694.  
  695.         Return Value :
  696.                 none
  697.  
  698.         Notes/Comments :
  699.                 See the warning above concerning the dire consequences
  700.                 of terminating without restoring the interrupt vectors
  701.                 and turning off multitasking.
  702.  
  703.  
  704.  
  705.  
  706.     LOADPRG(cmd_string,pri,expmemflag)
  707.     char *cmd_string;
  708.     int pri;
  709.     int expmemflag;
  710.  
  711.         Parameters :
  712.                 cmd_string is a character pointer to a command string
  713.                 to load the program, including command line parameters
  714.                 just as you would type in to load the program under
  715.                 DOS.
  716.  
  717.                 pri is the base priority of the new task
  718.  
  719.                 if expmemflag is true, the task is eligible to be loaded
  720.                 into expanded memory.  if set to 0, the task may not be
  721.                 loaded into expanded memory, even if START_SWAPPING()
  722.                 has been called previously.
  723.  
  724.         Description :
  725.                 Loads and executes an .EXE or .COM file from disk.
  726.                 The full path to the program file must be specified,
  727.                 and command line parameters may be used.
  728.  
  729.         Return Value :
  730.                  0 = no error
  731.                 -1 = not enough memory to create new task stack
  732.                 -2 = no free task control blocks
  733.                 -3 = no free memory control blocks
  734.  
  735.         Notes/Comments :
  736.                 This function performs the same service as LOADTASK()
  737.                 below.  The difference is that this service is called
  738.                 directly as a function.  LOADTASK is invoked via the
  739.                 INT 62H.  Functionally, there is no difference, since
  740.                 LOADTASK() eventually calls LOADPRG to do the work.
  741.  
  742.  
  743. Cswitch Global Variables
  744. ------------------------
  745.  
  746. The following variables are available to the main program.  These are
  747. provided for information only.
  748.  
  749.     int dos48, dos49, dos4a, dos4c;
  750.  
  751.         These integers represent a running count of how many DOS calls
  752.         (INT 21H) have been made for these function codes.
  753.  
  754.     unsigned int base_mem_segment;
  755.  
  756.         This is the segment value of the memory segment controlled by
  757.         Cswitch.
  758.  
  759.     int idlecount;
  760.  
  761.         This is a running count of how many times the idle task has
  762.         run.  The idle task is spawned when START_MT() is called, and
  763.         handles the tasks that are delaying and terminating.
  764.  
  765.     int swap_count;
  766.  
  767.         This is a running count of how many task switches have taken
  768.         place.  When the scheduler is called, this count is incremented
  769.         even if no task switch takes place due to a task locking out
  770.         switching or some other reason.
  771.  
  772.     int extmem_pages;
  773.  
  774.         This is the total number of expanded memory pages available
  775.         for task swapping. This variable is loaded after a successful
  776.         call to START_SWAPPING().
  777.  
  778.     int exmemfree;
  779.  
  780.         This is the number of free pages of expanded memory
  781.         left in the system.  Pages are allocated as needed by memory
  782.         allocation calls, so this number may vary somewhat during
  783.         operation.
  784.  
  785.  
  786. Cswitch Internal Services
  787. -------------------------
  788.  
  789. The following functions are provided in the file LMTC.OBJ and SMTC.OBJ,
  790. and may be called by any task executing under Cswitch control.  These
  791. functions allow access to the semaphore, messaging, and task control
  792. functions of Cswitch.
  793.  
  794. These functions are actually invoked via an INT 62H call.  The parameters
  795. are loaded into registers, and the INT 62H transfers control to an
  796. assembly language routine.  The registers are then pushed onto the stack,
  797. and the C function for system calls is executed.
  798.  
  799. Most of the work done by Cswitch is done by C routines, with assembly
  800. language used only for interrupt control.
  801.  
  802. All of these functions are of type INT, although some do not have return
  803. values assigned.
  804.  
  805. The assembly language interface is provided for those users not using
  806. Microsoft C or QuickC.  To invoke any of the services, simply set up the
  807. registers as shown and generate an INT62H  (see lmtca.asm for example).
  808. Return values are always in AX when services are invoked via INT62H.
  809.  
  810.  
  811.     RELINQ()
  812.  
  813.         Parameters :
  814.                 none
  815.  
  816.         Description :
  817.                 Gives up the rest of the tasks time slice.
  818.                 This is graceful way of not wasting processor time if
  819.                 your task has nothing else to do for a short while.
  820.  
  821.         Return Value :
  822.                 none
  823.  
  824.         Notes/Comments :
  825.                 none
  826.  
  827.         Assembler Interface :
  828.                 AH = 1
  829.  
  830.  
  831.     SPAWN(func_addr,pri)
  832.     int *func_addr();
  833.     int pri;
  834.  
  835.         Parameters :
  836.                 func_addr is the address of a function or subroutine
  837.                 that is to be executed as a separate task
  838.  
  839.                 pri is the base priority for the new task (1-10)
  840.  
  841.         Description :
  842.                 Causes the specified function or subroutine to be
  843.                 executed as a separate task.  A new stack is allocated
  844.                 for the task, allowing it to have exclusive access to
  845.                 local variables.  It will still share global variables
  846.                 with the calling task.
  847.  
  848.                 STDIN, STDOUT, and STDERR are inherited from the caller.
  849.  
  850.                 The function can be spawned more than once, allowing
  851.                 multiple tasks to execute the same code thread.
  852.  
  853.         Return Value :
  854.                 >0 = the tcb number of the new task, indicating no error
  855.                 -1 = not enough memory to create new task stack
  856.                 -2 = no free task control blocks
  857.                 -3 = no free memory control blocks
  858.  
  859.         Notes/Comments :
  860.                 A task that is swappable to expanded memory (i.e. one
  861.                 that was loaded after a call to START_SWAPPING() )
  862.                 cannot spawn new tasks.
  863.  
  864.         Assembler Interface :
  865.                 AH = 7
  866.                 DX:BX = func_addr
  867.                 CX = priority
  868.  
  869.  
  870.  
  871.     LOADTASK(cmd_string,pri,expmemflag)
  872.     LOADPIPE(cmd_string,pri,expmemflag)
  873.     EXECPROG(cmd_string,pri,expmemflag)
  874.     char *cmd_string;
  875.     int pri;
  876.     int expmemflag;
  877.  
  878.         Parameters :
  879.                 cmd_string is a character pointer to a command string
  880.                 to load the program, including command line parameters
  881.                 just as you would type in to load the program under
  882.                 DOS.
  883.  
  884.                 pri is the base priority of the new task
  885.  
  886.                 if expmemflag is non-zero, the task may be loaded into
  887.                 expanded memory, if it is enabled.  If exmemflag is
  888.                 0, the task will not be loaded into expanded memory.
  889.  
  890.         Description :
  891.                 Loads and executes an .EXE or .COM file from disk.
  892.                 The full path to the program file must be specified,
  893.                 and command line parameters may be used.
  894.  
  895.                 STDIN, STDOUT, and STDERR are inherited from the caller.
  896.  
  897.                 LOADPIPE forces the new program to send it's I/O to an
  898.                 internal pipe.  The pipe is identified by the new tasks
  899.                 TCB, so you must call GET_LOAD_STATUS() to obtain the
  900.                 TCB number (which is also the pipe number) for the new
  901.                 task.  At that point, you may send data to the new task
  902.                 and receive data from it via the SEND_PIPE and RECV_PIPE
  903.                 calls.
  904.  
  905.                 EXECPROG causes the calling task to be replaced by the
  906.                 new task.  No files or data are inherited, except STDIN,
  907.                 STDOUT, and STDERR.  If the calling task had a pipe, the
  908.                 new task inherits the pipe.  If the call is successful,
  909.                 the original task does not return from this call.
  910.  
  911.         Return Value :
  912.                  0 = task loader queue is full
  913.                  1 = no error
  914.  
  915.         Notes/Comments :
  916.                 This call actually places a load request onto a queue
  917.                 to be executed by the 'idle' task.  The status of the
  918.                 load may be checked with the TEST_LOAD() call.
  919.  
  920.                 .COM files are given exactly 64K memory.  .EXE files
  921.                 are sized according to the information in the file
  922.                 header.  The MINALLOC parameter is used to determine
  923.                 how much memory, in addition to the task size and
  924.                 data space, is needed for the program.
  925.  
  926.         Assembler Interface :
  927.                 AH = 15         (loadtask)
  928.              or AH = 22         (loadpipe)
  929.              or AH = 23         (execprog)
  930.                 ES:BX = cmd_string
  931.                 CX = priority
  932.                 DX = expmemflag
  933.  
  934.  
  935.     SLEEP(seconds)
  936.     int sleep;
  937.  
  938.         Parameters :
  939.                 seconds is the number of seconds the task is to remain
  940.                 alseep.
  941.  
  942.         Description :
  943.                 puts the calling task into a sleeping state, where it
  944.                 will not execute at all, for a specified number of
  945.                 seconds.
  946.  
  947.         Return Value :
  948.                 none
  949.  
  950.         Notes/Comments :
  951.  
  952.         Assembler Interface :
  953.                 AH = 5
  954.                 BX = seconds
  955.  
  956.  
  957.  
  958.     SEND_MD_MSG(queue,message_addr,count)
  959.     int queue;
  960.     char *message_addr;
  961.     int count;
  962.  
  963.         Parameters :
  964.                 queue is the message queue to place a message on
  965.                 message_addr is a pointer to the data to be placed on queue
  966.                 count is the number of bytes to place on the queue
  967.  
  968.         Description :
  969.                 places the specified number of bytes onto a message
  970.                 queue.  queues are numbered 0-63.
  971.  
  972.         Return Value :
  973.                 -1 = bad queue number
  974.                 other wise, returns number of bytes transferred
  975.  
  976.         Notes/Comments :
  977.  
  978.         Assembler Interface :
  979.                 AH = 11
  980.                 DS:SI = message_addr
  981.                 CX = length
  982.                 DX = queue number
  983.  
  984.  
  985.  
  986.     TESTMSG(queue)
  987.     int queue;
  988.  
  989.         Parameters :
  990.                 queue is the queue number to test
  991.  
  992.         Description :
  993.                 tests whether there are any messages waiting on the
  994.                 specified queue.
  995.  
  996.         Return Value :
  997.                 -1 = bad queue number
  998.                  0 = nothing on queue
  999.                  else, returns the number of bytes contained in the
  1000.                  first message on the queue
  1001.  
  1002.         Notes/Comments :
  1003.  
  1004.         Assembler Interface :
  1005.                 AH = 10
  1006.                 DX = queue number
  1007.  
  1008.  
  1009.  
  1010.     RECVMSG(queue,message_addr,count)
  1011.     int queue;
  1012.     char *message_addr;
  1013.     int count;
  1014.  
  1015.         Parameters :
  1016.                 queue is the message queue to place a message on
  1017.                 message_addr is a pointer a data area to receive the data
  1018.                 count is the maximum number of bytes to transfer
  1019.  
  1020.         Description :
  1021.                 reads the first message from the specified queue
  1022.                 into the data area pointed to by message_addr. only
  1023.                 the first 'count' bytes of the message are transferred.
  1024.                 if more than 'count' bytes are in the message, the
  1025.                 extra is lost.
  1026.  
  1027.                 If there are no messages on the specified queue, the
  1028.                 task is placed into a dormant state until a message is
  1029.                 put on the queue.  If you do not want your task to remain
  1030.                 dormant if the queue is empty, you should call TESTMSG()
  1031.                 to make sure something is available before calling this
  1032.                 function.
  1033.  
  1034.         Return Value :
  1035.                 -1 = bad queue number
  1036.                  0 = no message was on queue
  1037.                  else, returns the number of bytes transferred
  1038.  
  1039.         Notes/Comments :
  1040.  
  1041.         Assembler Interface :
  1042.                 AH = 12
  1043.                 DS:SI = message_addr
  1044.                 DX = queue number
  1045.                 CX = count
  1046.  
  1047.                 note : If return value is -2 (0fffeh), make the call again.
  1048.  
  1049.  
  1050.     SEND_PIPE(pipenum,message_addr,count)
  1051.     int pipenum;
  1052.     char *message_addr;
  1053.     int count;
  1054.  
  1055.         Parameters :
  1056.                 pipenum is the pipe (TCB) to send the message to
  1057.                 message_addr is a pointer to the data to be placed on queue
  1058.                 count is the number of bytes to place on the queue
  1059.  
  1060.         Description :
  1061.                 places the specified number of bytes onto a pipe
  1062.  
  1063.         Return Value :
  1064.                 -1 = bad pipe number
  1065.                 other wise, returns number of bytes transferred
  1066.  
  1067.         Notes/Comments :
  1068.  
  1069.         Assembler Interface :
  1070.                 AH = 20
  1071.                 DS:SI = message_addr
  1072.                 CX = length
  1073.                 DX = queue number
  1074.  
  1075.  
  1076.     RECV_PIPE(pipenum,message_addr,count)
  1077.     int pipenum;
  1078.     char *message_addr;
  1079.     int count;
  1080.  
  1081.         Parameters :
  1082.                 pipenum is the pipe (TCB) to receive data from
  1083.                 message_addr is a pointer to the data to be placed on queue
  1084.                 count is the maximum number of bytes to retrieve
  1085.  
  1086.         Description :
  1087.                 retrieves specified number of bytes from a pipe.
  1088.                 if there are fewer bytes in the pipe than you specify,
  1089.                 all bytes on the pipe will be returned.
  1090.                 if there are more bytes than you specify, then the
  1091.                 extra bytes will remain on the pipe, and can be
  1092.                 retrieved with another RECV_PIPE call.
  1093.  
  1094.         Return Value :
  1095.                 -1 = bad pipe number
  1096.                 other wise, returns number of bytes transferred
  1097.                 0 is a valid return, meaning the pipe was empty.
  1098.  
  1099.         Notes/Comments :
  1100.  
  1101.         Assembler Interface :
  1102.                 AH = 21
  1103.                 DS:SI = message_addr
  1104.                 CX = length
  1105.                 DX = queue number
  1106.  
  1107.  
  1108.     SEM_ATTACH(semaphore_number)
  1109.     int semaphore_number;
  1110.  
  1111.         Parameters :
  1112.                 semaphore number is the number of the semaphore to
  1113.                 attach (0-63)
  1114.  
  1115.         Description :
  1116.                 attaches to the semaphore.  see semaphore description
  1117.                 elsewhere in documentation
  1118.  
  1119.         Return Value :
  1120.                 -2 = invalid semaphore number
  1121.                 all other values = success
  1122.  
  1123.         Notes/Comments :
  1124.  
  1125.         Assembler Interface :
  1126.                 AH = 2
  1127.                 DX = semaphore number
  1128.  
  1129.  
  1130.     SEM_RELEASE(semaphore_number)
  1131.     int semaphore_number;
  1132.  
  1133.         Parameters :
  1134.                 semaphore number is the number of the semaphore to
  1135.                 be released (0-63)
  1136.  
  1137.         Description :
  1138.                 releases the semaphore.  see semaphore description
  1139.                 elsewhere in documentation
  1140.  
  1141.         Return Value :
  1142.                 -1 = this task is not attached to the semaphore
  1143.                 -2 = invalid semaphore number
  1144.                 all other values = success
  1145.  
  1146.         Assembler Interface :
  1147.                 AH = 4
  1148.                 DX = semaphore number
  1149.  
  1150.  
  1151.  
  1152.     SEM_TEST(semaphore_number)
  1153.     int semaphore_number;
  1154.  
  1155.         Parameters :
  1156.                 semaphore number is the number of the semaphore to
  1157.                 test (0-63)
  1158.  
  1159.         Description :
  1160.                 tests to see if the semaphore is already owned by
  1161.                 another task. see semaphore description
  1162.                 elsewhere in documentation
  1163.  
  1164.         Return Value :
  1165.                 -1 = semaphore is unowned
  1166.                 -2 = semaphore number is illegal
  1167.                 all other values = semaphore is owned
  1168.  
  1169.         Assembler Interface :
  1170.                 AH = 3
  1171.                 DX = semaphore number
  1172.  
  1173.  
  1174.  
  1175.  
  1176.     SETPRI(new_priority)
  1177.     int new_priority;
  1178.  
  1179.         Parameters :
  1180.                 new_priority is the new base priority of the calling
  1181.                 task.
  1182.  
  1183.         Description :
  1184.                 allows a task to change its priority, affecting how
  1185.                 often the task gets to execute.
  1186.  
  1187.         Return Value :
  1188.                 none
  1189.  
  1190.         Notes/Comments :
  1191.                 only values 1-10 are legal.  the lower the number, the
  1192.                 more often a task will run.  the main program is assigned
  1193.                 a prioroty of 1, and usually should be the only task with
  1194.                 such a low priority.
  1195.  
  1196.         Assembler Interface :
  1197.                 AH = 9
  1198.                 BX = priority
  1199.  
  1200.  
  1201.  
  1202.     SUSPEND()
  1203.  
  1204.         Parameters :
  1205.                 none
  1206.  
  1207.         Description :
  1208.                 suspends the task until it is awakened by a WAKEUP()
  1209.                 call.
  1210.  
  1211.         Return Value :
  1212.                 none
  1213.  
  1214.         Notes/Comments :
  1215.  
  1216.         Assembler Interface :
  1217.                 AH = 6
  1218.  
  1219.  
  1220.  
  1221.     WAKEUP(tcb_number)
  1222.     int tcb_number;
  1223.  
  1224.         Parameters :
  1225.                 tcb_number is the tcb identifier for the task that is
  1226.                 to be awakened.
  1227.  
  1228.         Description :
  1229.                 allows a task that was suspended with a SUSPEND() call
  1230.                 to continue processing.
  1231.  
  1232.         Return Value :
  1233.                 none
  1234.  
  1235.         Notes/Comments :
  1236.                 This is the ONLY way to wake up a SUSPENDed task.
  1237.                 This call will also wake up a task that is in a
  1238.                 SLEEP state (see SLEEP() above).  In this way, a task
  1239.                 may go into a sleep state, and another task can cause
  1240.                 it to start executing again before the sleep delay is
  1241.                 over.
  1242.  
  1243.         Assembler Interface :
  1244.                 AH = 8
  1245.                 BX = tcb_number
  1246.  
  1247.  
  1248.  
  1249.     HOG()
  1250.  
  1251.         Parameters :
  1252.                 none
  1253.  
  1254.         Description :
  1255.                 prevents this task from being swapped out until a
  1256.                 corresponding NOHOG() is issued.  In effect, this
  1257.                 halts multitasking, allowing the caller to run
  1258.                 forever, if desired.
  1259.  
  1260.         Return Value :
  1261.                 none
  1262.  
  1263.         Notes/Comments :
  1264.                 Use this function when your task must not be interrupted.
  1265.                 DOS functions like I/O and other critical functions are
  1266.                 already protected, so this function should not be used
  1267.                 too often.
  1268.  
  1269.         Assembler Interface :
  1270.                 AH = 13
  1271.  
  1272.  
  1273.  
  1274.     NOHOG()
  1275.  
  1276.         Parameters :
  1277.                 none
  1278.  
  1279.         Description :
  1280.                 allows multitasking to continue after being halted by
  1281.                 a HOG() call.
  1282.  
  1283.         Return Value :
  1284.                 none
  1285.  
  1286.         Notes/Comments :
  1287.  
  1288.         Assembler Interface :
  1289.                 AH = 14
  1290.  
  1291.  
  1292.  
  1293.     SPAWN_EXIT()
  1294.  
  1295.         Parameters :
  1296.                 none
  1297.  
  1298.         Description :
  1299.                 halts execution of a SPAWNed task.
  1300.  
  1301.         Return Value :
  1302.                 none
  1303.  
  1304.         Notes/Comments :
  1305.                 this is the preferred method of halting execution of a
  1306.                 spawned task.  When the spawned task is finished, it
  1307.                 should call this function to terminate.
  1308.  
  1309.  
  1310.         Assembler Interface :
  1311.                 AH = 16
  1312.  
  1313.  
  1314.  
  1315.     GET_TCB_INFO(pointer_address)
  1316.     struct tcb_rec far **pointer_address;
  1317.  
  1318.         Parameters :
  1319.                 pointer_address is the address of a pointer into
  1320.                 which the address of the calling tasks TCB will be placed.
  1321.  
  1322.         Description :
  1323.                 this function is used to access your Task Control Block
  1324.                 you pass in the address of a pointer, and the address
  1325.                 of your TCB is placed into the pointer.  The pointer
  1326.                 must be a LONG pointer, as supported in the LARGE
  1327.                 memory model.
  1328.  
  1329.         Return Value :
  1330.                 the TCB identifier (an index into the TCB array) is
  1331.                 returned.
  1332.  
  1333.  
  1334.         Notes/Comments :
  1335.                 Normally, tasks do not need access to their TCB.  Since
  1336.                 TCBs contain critical task control information, great
  1337.                 care must be taken when accessing the TCB.
  1338.  
  1339.         Assembler Interface :
  1340.                 AH = 17
  1341.                 ES:BX = address of pointer to receive TCB address
  1342.  
  1343.  
  1344.     GET_TCB_ADDRESS(pointer_address)
  1345.     struct tcb_rec far **pointer_address;
  1346.  
  1347.         Parameters :
  1348.                 pointer_address is the address of a pointer into
  1349.                 which the address of the TCB array will be placed.
  1350.  
  1351.         Description :
  1352.                 similar to 'GET_TCB_INFO()' above, but this call
  1353.                 returns the address of the TCB array in memory, to
  1354.                 allow your task to access any given TCB, based on
  1355.                 the task number.
  1356.  
  1357.         Return Value :
  1358.                 the TCB identifier (an index into the TCB array) of
  1359.                 the calling task returned.
  1360.  
  1361.  
  1362.         Notes/Comments :
  1363.                 Normally, tasks do not need access to their TCB.  Since
  1364.                 TCBs contain critical task control information, great
  1365.                 care must be taken when accessing the TCB.
  1366.  
  1367.         Assembler Interface :
  1368.                 AH = 18
  1369.                 ES:BX = address of pointer to receive TCB address
  1370.  
  1371.  
  1372.  
  1373.     GET_LOAD_STATUS()
  1374.  
  1375.         Parameters :
  1376.                 none
  1377.  
  1378.         Description :
  1379.                 returns the status of the last 'LOAD_TASK()' call
  1380.                 made by your program.
  1381.  
  1382.         Return Value :
  1383.                  0 = load in progress (not done yet)
  1384.                 -1 = cannot open file
  1385.                 -2 = insufficient memory
  1386.                 -3 = no TCBs available
  1387.                 -4 = no Memory Control Blocks available
  1388.  
  1389.                 otherwise, the TCB number (index into the TCB array)
  1390.                 of the new task is returned.
  1391.  
  1392.         Notes/Comments :
  1393.  
  1394.         Assembler Interface :
  1395.                 AH = 19
  1396.  
  1397.  
  1398.  
  1399. Sample Programs
  1400. ---------------
  1401.  
  1402. 2 sample program are included, with full source code.
  1403. MTTEST.C will load and execute the programs TEST1.EXE, TEST2.EXE,
  1404. TEST3.EXE, TEST4.EXE and TEST5.EXE.  These 5 programs read
  1405. and write a disk file.  Each time they finish reading and writing
  1406. the file, they send a message to MTTEST.  MTTEST receives and
  1407. displays the messages.  It's not particularly impressive to
  1408. watch, but it does illustrate that several programs can perform
  1409. concurrent I/O, and that programs can easily be loaded and executed
  1410. from disk.
  1411.  
  1412. MT.C is another sample program.  It demonstrates one use of the
  1413. semaphores, and the sleep() function.
  1414.  
  1415. The source files for these programs, and a make file for Microsoft
  1416. C is included.
  1417.  
  1418.  
  1419. Writing Programs With CSWITCH
  1420. -----------------------------
  1421.  
  1422. When you write your multitasking program, there are several things
  1423. to remember :
  1424.  
  1425. 1. You must use the LARGE memory model.  Programs that are linked with
  1426.    SMTC, or that are simply loaded and run, may be small model.  But
  1427.    your main program must be LARGE model.
  1428.  
  1429. 2. You must compile your main program, and any program that will SPAWN
  1430.    tasks, with the /Gs option to turn off stack checking.  This is
  1431.    imperative, and without it, your program will not run.
  1432.  
  1433. 3. C library routines are not re-entrant.  Therefore, you cannot
  1434.    do a PRINTF() from your main program, and one from a SPAWNED task
  1435.    at the same time.  In general, if a spawned task needs to do I/O,
  1436.    do it with DOS interrupts rather than with the library calls.  If
  1437.    this is impractical or impossible, then make the spawned function
  1438.    a separate program and execute it with LOADTASK().
  1439.  
  1440.    If several different programs perform simultaneous I/O there is no
  1441.    problem.  It is only when the same PRINTF or FOPEN routines are
  1442.    called from 2 different places in the same program at the same time
  1443.    that there is a problem.
  1444.  
  1445.    This probably applies to other languages as well.
  1446.  
  1447.    Also, most library routines perform stack checking upon entry.  Since
  1448.    a SPAWNED task is assigned a stack from free memory, the stack check
  1449.    will often fail, causing SPAWNED tasks to abort with a false stack
  1450.    overflow error.  If SPAWNed tasks are going to be performing complex
  1451.    functions and making a lot of library calls, they probably should
  1452.    be made into separate programs and executed with LOADTASK().
  1453.  
  1454.  
  1455. Language Support
  1456. ----------------
  1457.  
  1458. Currently, the only language directly supported is C.  Most languages
  1459. can be made to mimic C's function calls, so there should not be a
  1460. problem with linking these routines to other languages.
  1461.  
  1462. Soon, I will be adding interface modules for Turbo Pascal and QuickBasic.
  1463.  
  1464. Identifiers are prefixed with an underscore ( _ ) by the C compiler, so
  1465. if your compiler does not automatically add the underscore, you will have
  1466. to use it explicitly in the module names, and in the global data names.
  1467.  
  1468.  
  1469. Cswitch Limitations
  1470. -------------------
  1471.  
  1472. Cswitch routines will not be appropriate in every situation requiring
  1473. multitasking.  In some cases, products like Desqview, Omniview, Windows,
  1474. OS/2, and Unix are more appropriate.  Cswitch does give a programmer
  1475. an option, however.  Writing programs that multitask in the DOS
  1476. environment is a clean solution to many problems.  In general, if your
  1477. application must somehow do several things all at once, then writing
  1478. your application as a set of cooperating tasks is a lot easier than
  1479. trying to build every function into a single task.  Let your
  1480. imagination take it from there.
  1481.  
  1482. As stated previously, Cswitch does not attempt to provide a multi-
  1483. tasking user interface.  You must prevent tasks from writing to the
  1484. monitor or reading the keyboard simultaneously.  This can easily be
  1485. done with semaphores or pipes.
  1486.  
  1487. Tasks that are created using the SPAWN call inherit their owners STDIN,
  1488. STDOUT and STDERR, as do tasks loaded from disk.  Normally these handles
  1489. refer to the CON device - the keyboard and monitor.  No other file
  1490. handles are inherited.
  1491.  
  1492.